/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.thesis.VeloEstiCellID;

import com.thesis.algorithm.Device;
import com.thesis.algorithm.Sample;
import com.thesis.algorithm.Sequence;
import com.thesis.algorithm.SequenceElement;
import com.thesis.utils.CoordinateConversion;
import com.thesis.utils.Utils;

import java.io.IOException;
import java.io.Writer;
import java.util.LinkedList;

/**
 *
 * @author Aya Hikaru
 */
public class VeloEstiCellID {
    // Using same sequence with distance saved in State(char) and counted in m.

    private boolean debug = false;
    public LinkedList<Sequence> allCellIDSeq = null;

    static public LinkedList<Sequence> produceTestSequence(LinkedList<Device> devices) {
        LinkedList<Sequence> output = new LinkedList<Sequence> ();
        Device tmpD = null;
        Sample tmpS = null;
        Sequence tmpSeq = null;
        SequenceElement tmpSeqEl = null;

        for (int i = 0; i < devices.size(); i++) {
            tmpD = devices.get(i);
            System.out.println("PROCESSING CASE BUSNO: " + tmpD.busNumber);
            tmpSeq = new Sequence();
            tmpSeq.BusRouteID = (int)tmpD.busNumber;
            tmpSeqEl = new SequenceElement();
            for (int j = 0; j < tmpD.sampleList.size(); j++) {
                tmpS = tmpD.get(j);
                if (j == 0) {
                    tmpSeqEl.Observation = (char) Integer.parseInt(tmpS.cellList.getFirst().id);
                    tmpSeqEl.Lac = (char) Integer.parseInt(tmpS.cellList.getFirst().lac);
                } else {
                    if (tmpSeqEl.Observation == ((char) Integer.parseInt(tmpS.cellList.getFirst().id))
                            && tmpSeqEl.Lac == ((char) Integer.parseInt(tmpS.cellList.getFirst().lac))) {
                        tmpSeqEl.Repetition++;
                    } else {
                        tmpSeq.add(tmpSeqEl);
                        tmpSeqEl = new SequenceElement();
                        tmpSeqEl.Observation = (char) Integer.parseInt(tmpS.cellList.getFirst().id);
                        tmpSeqEl.Lac = (char) Integer.parseInt(tmpS.cellList.getFirst().lac);
                    }

                }
            }
            tmpSeq.add(tmpSeqEl);
            output.add(tmpSeq);
            if (tmpSeq.score() != tmpD.sampleList.size())
            {
                System.out.println("tmpSeq.score(): " + tmpSeq.score() + " tmpSeq.size() :"+ tmpSeq.size() + "tmpD.sampleList.size(): "+ tmpD.sampleList.size());
            }
        }
        return output;
    }
    
    public VeloEstiCellID(LinkedList<Device> devices) {
        Device tmpD = null;
        Sample tmpS = null;
        double distance = 0;
        Sequence tmpSeq = null;
        SequenceElement tmpSeqEl = null;
        this.allCellIDSeq = new LinkedList<Sequence>();
        double prevLng = 0;
        double prevLat = 0;
        double currLng = 0;
        double currLat = 0;

        for (int i = 0; i < devices.size(); i++) {
            tmpD = devices.get(i);
            System.out.println("PROCESSING CASE BUSNO: " + tmpD.busNumber);
            tmpSeq = new Sequence();
            tmpSeq.BusRouteID = (int)tmpD.busNumber;
            tmpSeqEl = new SequenceElement();
            for (int j = 0; j < tmpD.sampleList.size(); j++) {
                tmpS = tmpD.get(j);
                currLat = Double.parseDouble(tmpS.lat);
                currLng = Double.parseDouble(tmpS.lng);
                if (debug) {
                    System.out.println("j = " + j + " observation " + tmpS.cellList.getFirst().id + " lac " + tmpS.cellList.getFirst().lac + " distance " + distance);
                    System.out.println("prev Lat : " + prevLat + " prev Lng : " + prevLng + "curr Lat : " + currLat + " curr Lng : " + currLng);
                }
                if (j == 0) {
                    tmpSeqEl.Observation = (char) Integer.parseInt(tmpS.cellList.getFirst().id);
                    tmpSeqEl.Lac = (char) Integer.parseInt(tmpS.cellList.getFirst().lac);
                    distance = 0;
                    if (debug) {
                        System.out.println("CASE j = 0: tmpSeqEl: observation " + (int) tmpSeqEl.Observation + " lac " + (int) tmpSeqEl.Lac + " repetition " + (int) tmpSeqEl.Repetition + " distance " + distance);
                    }
                } else {
                    distance += CoordinateConversion.LatLngDistance(currLat, currLng, prevLat, prevLng);
                    if (tmpSeqEl.Observation == ((char) Integer.parseInt(tmpS.cellList.getFirst().id))
                            && tmpSeqEl.Lac == ((char) Integer.parseInt(tmpS.cellList.getFirst().lac))) {
                        tmpSeqEl.Repetition++;
                        if (debug) {
                            System.out.println("CASE SAME: tmpSeqEl: observation " + (int) tmpSeqEl.Observation + " lac " + (int) tmpSeqEl.Lac + " repetition " + (int) tmpSeqEl.Repetition + " distance " + distance);
                        }
                    } else {
                        tmpSeqEl.State = (char) (distance * 1000);
                        if (debug) {
                            System.out.println("CASE NOT SAME BEFORE: tmpSeqEl: observation " + (int) tmpSeqEl.Observation + " lac " + (int) tmpSeqEl.Lac + " repetition " + (int) tmpSeqEl.Repetition + " distance " + distance);
                        }
                        tmpSeq.add(tmpSeqEl);
                        distance = 0;
                        tmpSeqEl = new SequenceElement();
                        tmpSeqEl.Observation = (char) Integer.parseInt(tmpS.cellList.getFirst().id);
                        tmpSeqEl.Lac = (char) Integer.parseInt(tmpS.cellList.getFirst().lac);
                        if (debug) {
                            System.out.println("CASE NOT SAME AFTER: tmpSeqEl: observation " + (int) tmpSeqEl.Observation + " lac " + (int) tmpSeqEl.Lac + " repetition " + (int) tmpSeqEl.Repetition + " distance " + distance);
                        }
                    }

                }

                prevLat = currLat;
                prevLng = currLng;
            }

            tmpSeqEl.State = (char) (distance * 1000);
            if (debug) {
                System.out.println("CASE FINAL: tmpSeqEl: observation " + (int) tmpSeqEl.Observation + " lac " + (int) tmpSeqEl.Lac + " repetition " + (int) tmpSeqEl.Repetition + " distance " + distance);
            }
            tmpSeq.add(tmpSeqEl);
            this.allCellIDSeq.add(tmpSeq);
            if (tmpSeq.score() != tmpD.sampleList.size())
            {
                System.out.println("tmpSeq.score(): " + tmpSeq.score() + " tmpSeq.size() :"+ tmpSeq.size() + "tmpD.sampleList.size(): "+ tmpD.sampleList.size());
            }
        }
    }

//    public int returnTimeInterval(int fileindex, int start, int end) {
//        Sequence tmp = this.allCellIDSeq.get(fileindex);
//        if (start < 0 || end < 0 || end >= tmp.size() || start >= tmp.size() || start > end) {
//            return 0;
//        }
//        int count = 0;
//        for (int i = start; i <= end; i++) {
//            count += tmp.get(i).Repetition;
//        }
//        return count;
//    }

    // return in km;
    public double returnDistance(int fileindex, int start, int end) {
        Sequence tmp = this.allCellIDSeq.get(fileindex);
        if (start < 0 || end < 0 || end >= tmp.size() || start >= tmp.size() || start > end) {
            return 0;
        }
        double distance = 0;
        for (int i = start; i <= end; i++) {
            distance += tmp.get(i).State;
        }
        return (distance/1000);
    }

//    public double returnVelocity(int fileindex, int start, int end) {
//        int time = returnTimeInterval(fileindex, start, end);
//        double distance = returnDistance(fileindex, start, end);
//        if (time == 0 || distance < 0.00000001)
//            return 0;
//        else
//            return (distance/time);
//    }

//    public double returnVelocityCellIDCount(int fileindex, int start, int end, int CellIDCount) {
//        if (start > (end-CellIDCount))
//            return 0;
//        return returnVelocity(fileindex, end-CellIDCount, end);
//    }

    public double returnDistanceTimeInterval(int fileindex, int start, int end, char partofStart, char partofEnd) {
        Sequence tmp = this.allCellIDSeq.get(fileindex);
        if (start < 0 || end < 0 || end >= tmp.size() || start >= tmp.size() || start > end) {
            return 0;
        }
        double distance = 0;
        
        for (int i = start; i <= end; i++) {
            if (i == start) {
                if (partofStart >= tmp.get(i).Repetition)
                    distance += tmp.get(i).State;
                else
                    distance += (tmp.get(i).State * 1.0 / tmp.get(i).Repetition * partofStart);
            }
            else if (i == end) {
                if (partofEnd >= tmp.get(i).Repetition)
                    distance += tmp.get(i).State;
                else
                    distance += (tmp.get(i).State * 1.0 / tmp.get(i).Repetition * partofEnd);
            }
            else {
                distance += tmp.get(i).State;
            }
        }
        return (distance/1000);
    }
    
    
    //=====================================
    //=== COPY from SEQUENCE MATCHING - DELETE WHEN CLEANING CODE
    //======================================
	public static Sequence lcs(Sequence a, Sequence b) {

		
		char[][] lengths = new char[a.size() + 1][b.size() + 1];
		// row 0 and column 0 are initialized to 0 already
		for (int i = 0; i < a.size(); i++)
			for (int j = 0; j < b.size(); j++)
				if (a.get(i).Observation == b.get(j).Observation)
					lengths[i + 1][j + 1] = (char) (lengths[i][j] + 1);
				else
					lengths[i + 1][j + 1] = (char) Math.max(lengths[i + 1][j],
							lengths[i][j + 1]);

		// read the substring out from the matrix
		Sequence result = new Sequence();
		for (int x = a.size(), y = b.size(); x != 0 && y != 0;) {
			if (lengths[x][y] == lengths[x - 1][y])
				x--;
			else if (lengths[x][y] == lengths[x][y - 1])
				y--;
			else {
				char scoreA = a.get(x - 1).Repetition;
				int scoreB = b.get(y - 1).Repetition; // testdata, must be int,
														// because char is
														// unsigned
				int index = 0;
				int xx = x - 1;
				while ((scoreB > 0) && (xx < a.size())) {
					if (a.get(xx).Observation != b.get(y - 1).Observation) {
						// TODO: check more about score here
						// result.sequenceinFile.getLast().Repetition+=scoreB;
						break;
					}
					SequenceElement se = new SequenceElement(a.get(xx));

					scoreB -= se.Repetition;
					if (scoreB < 0)
						se.Repetition += scoreB;
					result.sequenceinFile.add(index, se);

					xx += 1;
					++index;
				}
				x--;
				y--;

			}
		}
		// TODO: check if needed
		if (a.BusRouteID != 0)
			result.BusRouteID = a.BusRouteID;
		else if (b.BusRouteID != 0)
			result.BusRouteID = b.BusRouteID;
		return result;
	}
	
    //=====================================
    //=== MATCHING
    //======================================
	// fileindex, start, end
	public int[] match(Sequence sequence, Writer out) throws IOException
	{
		sequence = sequence.refineSequence();
		Sequence[] matchedSeqs = new Sequence[allCellIDSeq.size()];
		int score[] = new int[allCellIDSeq.size()];
		
		for (int i = 0; i < allCellIDSeq.size(); i++) {
				matchedSeqs[i] = lcs(allCellIDSeq.get(i), sequence);
				score[i] = matchedSeqs[i].score();
		}
		
	
		int[] matchedBusroute = Utils.findAllMaxInArray(score);
		for (int i = 0; i < matchedBusroute.length; i++) {
			if (matchedSeqs[matchedBusroute[i]] != null) {
				System.out.println(matchedSeqs[matchedBusroute[i]].BusRouteID + " " + matchedSeqs[matchedBusroute[i]].score());
			}
		}
		
		
		//process index, START, END
		//TODO: multiple matches
		//TODO: xem lai cach cat TEST DATA - LOGIC sai
		int index = Utils.positionOfMax(score);
		int startV= 0;
		int endV =0;
		Sequence interestedSeq = allCellIDSeq.get(index);
		if (matchedBusroute.length !=1)
		{
			out.write("THIS CASE HAS MULTIPLE MATCHES\n");
		}
		out.write(interestedSeq.toString()+"\n");
		
		for (int i = 0; i < interestedSeq.size(); i++) {
			if (sequence.getFirst().Observation == interestedSeq.get(i).Observation)
				startV =i;
			if (sequence.getLast().Observation == interestedSeq.get(i).Observation)
				endV =i;
		}
		
		if (endV==0 || startV==0)
		{
			out.write("THIS CASE NEED FURTHER OPTIMIZATION");
		}
		
		out.write("matching result - busNo: " + (int)interestedSeq.BusRouteID +"\n");
		
		int[] result =  new int[3];
		result[0] =index;
		result[1] =startV;
		result[2] =endV; 
		return result;
		
	}
}
